package com.fast.develop.configuration;

import com.fast.develop.api.FastDevelopConfiguration;
import com.fast.develop.configuration.annotation.EnableDomain;
import com.fast.develop.configuration.registor.MetaDomainDbRegistor;
import com.fast.develop.configuration.persistence.DbResourceRepositoryImpl;
import com.fast.develop.configuration.persistence.ResourceFileRepository;
import com.fast.develop.configuration.properties.FastDevelopPropesties;
import com.fast.develop.core.io.ResourceManager;
import com.fast.develop.core.io.db.DbResourceManager;
import com.fast.develop.core.io.db.DbResourceRepository;
import com.fast.develop.core.io.disk.DiskResourceManager;
import com.fast.develop.core.plugin.PluginRegister;
import com.fast.develop.integration.web.MvcMappingHandlerMapping;
import com.fast.develop.meta.dm.MetaDomainRegistor;
import com.fast.develop.meta.resource.MetaResourceRegistor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.UnsupportedEncodingException;
import java.util.*;


/**
 * @author 莫寅
 * 领域模型自动装配
 */
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties({FastDevelopPropesties.class})
@Slf4j
@Import({FastJpaRepositoryConfiguration.class})
//@Import({FastJpaRepositoryConfiguration.class, FastDevelopWebConfiguration.class})
@ConditionalOnClass({RequestMappingHandlerMapping.class})
public class FastDevelopAutoConfiguration implements ImportAware {


    private DataSource dataSource;

    private FastDevelopPropesties fastDevelopPropesties;

    protected RequestMappingHandlerMapping requestMappingHandlerMapping;

    private Map annotationProperties;

    private FastDevelopConfiguration fastDevelopConfiguration;

    /**
     * 请求拦截器
     */
    private final ObjectProvider<List<PluginRegister>> luginRegisterProvider;

    private ResourceManager resourceManager;

    private String resourcePath;


    private ResourceFileRepository resourceFileRepository;

    public FastDevelopAutoConfiguration(
            ResourceFileRepository resourceFileRepository,
            DataSource dataSource,
            FastDevelopPropesties fastDevelopPropesties,
            RequestMappingHandlerMapping requestMappingHandlerMapping,
            ObjectProvider<List<PluginRegister>> luginRegisterProvider
    ) {
        this.luginRegisterProvider = luginRegisterProvider;
        this.dataSource = dataSource;
        this.fastDevelopPropesties = fastDevelopPropesties;
        this.requestMappingHandlerMapping = requestMappingHandlerMapping;
        this.resourceFileRepository = resourceFileRepository;

        //加载模式
        initRuntimeMode();
    }




    private void initRuntimeMode(){
        if(fastDevelopPropesties.getRuntimeMode() == FastDevelopPropesties.RuntimeMode.FILE){
            initDiskResourceManager();
        }else if(fastDevelopPropesties.getRuntimeMode() == FastDevelopPropesties.RuntimeMode.DATABASE){
            initDbResourceManager();
        }
    }

    @SneakyThrows
    private void initDiskResourceManager(){
        initCurrentResourcePath();
        this.resourceManager = new DiskResourceManager(resourcePath);


        FastDevelopConfiguration fastDevelopConfiguration = new FastDevelopConfiguration(
                resourceManager,
                dataSource,
                requestMappingHandlerMapping,
                luginRegisterProvider
        );
        //增加元数据注册器
        fastDevelopConfiguration
                .addMetaRegistor(new MetaDomainRegistor())
                .addMetaRegistor( new MetaResourceRegistor());

        this.fastDevelopConfiguration = fastDevelopConfiguration;
    }

    @SneakyThrows
    private void initDbResourceManager(){
        DbResourceRepository dbResourceRepository = new DbResourceRepositoryImpl(resourceFileRepository);
        DbResourceManager dbResourceManager = new DbResourceManager(dbResourceRepository);
        this.resourceManager = dbResourceManager;

        FastDevelopConfiguration fastDevelopConfiguration = new FastDevelopConfiguration(
                resourceManager,
                dataSource,
                requestMappingHandlerMapping,
                luginRegisterProvider
        );
        //增加元数据注册器
        fastDevelopConfiguration
                .addMetaRegistor(new MetaDomainDbRegistor(dbResourceRepository))
                .addMetaRegistor( new MetaResourceRegistor());

        this.fastDevelopConfiguration = fastDevelopConfiguration;
    }

    private void initCurrentResourcePath() throws UnsupportedEncodingException {
        String resourcePath = Class.class.getClass().getResource("/").getPath().split("target/classes")[0];
        resourcePath += "src/main/resources/.schema/domain";
        resourcePath = java.net.URLDecoder.decode(resourcePath, "utf-8");
        this.resourcePath = resourcePath;
    }

    @Bean
    public MvcMappingHandlerMapping mvcMappingHandlerMapping(){
        return fastDevelopConfiguration.getMvcMappingHandlerMapping();
    }

    @Bean
    public FastDevelopCommandLineRunner fastDevelopStartedConfiguration(ApplicationContext applicationContext, FastDevelopPropesties fastDevelopPropesties){
        return new FastDevelopCommandLineRunner(applicationContext, fastDevelopPropesties);
    }

    @Bean
    public FastDevelopConfiguration fastDevelopConfiguration(){
        fastDevelopConfiguration
                .domainProperties(fastDevelopPropesties.getDomain())
                .packagesToScan(getPackagesToScan())
                .annotatedClasses(getAnnotatedClasses())
                .mappingResources(getMappingResources())
                .showSql(fastDevelopPropesties.getDomain().isShowSql())
                .ddlAuto(fastDevelopPropesties.getDomain().getDdlAuto());
        return fastDevelopConfiguration;
    }



    private String[] getPackagesToScan(){
        if(annotationProperties == null){
            return fastDevelopPropesties.getDomain().getPackagesToScan();
        }

        LinkedList packagesToScan = ((LinkedList)annotationProperties.get("packagesToScan"));
        if(((String[])packagesToScan.get(0)).length != 0){
            return (String[]) packagesToScan.get(0);
        }else{
            return fastDevelopPropesties.getDomain().getPackagesToScan();
        }
    }


    private Class[] getAnnotatedClasses(){
        if(annotationProperties == null){
            return fastDevelopPropesties.getDomain().getAnnotatedClasses();
        }

        LinkedList annotatedClasses = ((LinkedList)annotationProperties.get("annotatedClasses"));
        if(((Class[])annotatedClasses.get(0)).length != 0){
            return (Class[]) annotatedClasses.get(0);
        }else{
            return fastDevelopPropesties.getDomain().getAnnotatedClasses();
        }
    }

    private String[] getMappingResources(){
        if(annotationProperties == null){
            return fastDevelopPropesties.getDomain().getMappingResources();
        }

        LinkedList mappingResources = ((LinkedList)annotationProperties.get("mappingResources"));
        if(((String[])mappingResources.get(0)).length != 0){
            return (String[]) mappingResources.get(0);
        }else{
            return fastDevelopPropesties.getDomain().getMappingResources();
        }
    }

    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        annotationProperties = importMetadata.getAllAnnotationAttributes(EnableDomain.class.getName());
    }

}
